From 1076ac3f8edf5a2b892bb20463f9dbf31e8349e7 Mon Sep 17 00:00:00 2001 From: parkrrrr Date: Thu, 28 Apr 2005 15:32:16 +0000 Subject: [PATCH] Add TomTom .ov2 (POI) file support --- gpsbabel/Makefile | 3 +- gpsbabel/README | 28 ++- gpsbabel/reference/ov2-arc-out.ref | Bin 0 -> 2535 bytes gpsbabel/reference/ov2-geo-out.ref | Bin 0 -> 416 bytes gpsbabel/reference/ov2-in.ref | 9 + gpsbabel/testo | 16 ++ gpsbabel/tomtom.c | 338 +++++++++++++++++++++++++++++ 7 files changed, 389 insertions(+), 5 deletions(-) create mode 100644 gpsbabel/reference/ov2-arc-out.ref create mode 100644 gpsbabel/reference/ov2-geo-out.ref create mode 100644 gpsbabel/reference/ov2-in.ref create mode 100644 gpsbabel/tomtom.c diff --git a/gpsbabel/Makefile b/gpsbabel/Makefile index 600221e8a..dbd766034 100644 --- a/gpsbabel/Makefile +++ b/gpsbabel/Makefile @@ -27,7 +27,7 @@ FMTS=magproto.o gpx.o geo.o mapsend.o mapsource.o garmin_tables.o \ gpilots.o saroute.o navicache.o psitrex.o geoniche.o delgpl.o \ ozi.o nmea.o text.o html.o palmdoc.o netstumbler.o hsa_ndv.o \ igc.o brauniger_iq.o shape.o hiketech.o glogbook.o coastexp.o \ - vcf.o overlay.o kml.o google.o lowranceusr.o an1.o + vcf.o overlay.o kml.o google.o lowranceusr.o an1.o tomtom.o FILTERS=position.o duplicate.o arcdist.o polygon.o smplrout.o reverse_route.o sort.o stackfilter.o @@ -213,6 +213,7 @@ text.o: text.c defs.h queue.h gbtypes.h jeeps/gpsmath.h jeeps/gps.h \ jeeps/gpsinput.h jeeps/gpsproj.h jeeps/gpsnmeafmt.h jeeps/gpsnmeaget.h tiger.o: tiger.c defs.h queue.h gbtypes.h csv_util.h tmpro.o: tmpro.c defs.h queue.h gbtypes.h csv_util.h +tomtom.o: tomtom.c defs.h queue.h gbtypes.h tpg.o: tpg.c defs.h queue.h gbtypes.h jeeps/gpsmath.h jeeps/gps.h \ jeeps/gpsport.h jeeps/gpsserial.h jeeps/gpssend.h jeeps/gpsread.h \ jeeps/gpsutil.h jeeps/gpsapp.h jeeps/gpsprot.h jeeps/gpscom.h \ diff --git a/gpsbabel/README b/gpsbabel/README index 2af698072..c2a873711 100644 --- a/gpsbabel/README +++ b/gpsbabel/README @@ -784,10 +784,30 @@ THE FORMATS gpsbabel -i an1 -f one.an1 -f two.an1 -o an1 -F merged.an1 - Currently, GPSBabel only writes drawing layers. If your input - file is a road, track, trail, or waypoint layer, you should not - attempt to write to an .an1 file as the results may be - unpredictable. + In this case, the merged data will contain all of the properties + of the original data. + + Currently, GPSBabel only writes drawing layers, as opposed to road, + track, and other specialized layers. If your input file is a road, + track, trail, or waypoint layer, you should not attempt to write to + an .an1 file as the results may be unpredictable. Note that this + also applies to merging files, so you can't currently merge two road + layers with GPSBabel (officially; there is an unsupported "type" + option that works in limited cases.) + + TomTom + + This format can read and write TomTom .ov2 (POI) files, as used by the + TomTom GO and TomTom Navigator. It has been tested with an original + TomTom GO running version 5.00 of the TomTom software. There may be + some records that confuse the input module - if you have an example + of such a record "in the wild", and you aren't restricted from sharing + it, we encourage you to post to the gpsbabel-misc mailing list to + contact a developer. + + Note that in addition to the .ov2 file, you will need a .bmp file for + the icon. It should be 22x22 and 16 colors, and have the same name + (not including the extension) as the .ov2 file. DATA FILTERS diff --git a/gpsbabel/reference/ov2-arc-out.ref b/gpsbabel/reference/ov2-arc-out.ref new file mode 100644 index 0000000000000000000000000000000000000000..f858319868c7654b36c3cefc4bbdb31567b9b091 GIT binary patch literal 2535 zcmZwIPe>GD7zXfJ@!~-Vk|;WuLJ);I>$>jlX)P_KI+zMcCM026Mg~FTkWx@YV&tJN zSsp#)vB2^Wi6ox7MP#WZ>1Ka4)3iV8^t?0AyE_9N_qXrNe$VrLI}=D`7)DQ}W%mV2 zjab~WS7+l!U@G0k=yE)T^=U3{{p`VQ0mGO%VA)pciw|3D`u!U9`z!=3sqlEN~uHsK#Q%vemd~ zv0OElAofUuu!IGeEKD}Zq_tI8b~+Xb?YdyV%S@EX@*dyQ+pjS`agV>X{fj6fTY zmYz2$E!p7IH}|{=It^;A&I)-H#CGbc*{WD8EyS(!854J5Zbj#qSgi7k+d_zmZ@})V zG4XX+LXC;9!7{({S|+{mR+h}~3Uj!xJcHRkAm?M4Zbx0pFD!z{I6sw{0#N^{C; z$Dn+0E@rq(u#@vNhPwcpQDeAsu%-phFat=a|E1%y9^FJMh?&ykX`z43lCU3mbtYKHl`1C*LK{VC!G^ zxH%~Q;%fX!yy(=LSB^cwq}(#fpRR^Y|Dkb-XJD_?8089VM2(Se!3Jp&R`VQoLX8!U z!D`f4^)f6&ja!nn-?GPlyK9MX>vCaF)L2actY3{e+F^Utn4=fA_>1>rjyc#xH8XVv zr7S5;8g$B%4KvPX3LCba9SNFjdLGuP#5tgaOaNEl)dvuNWW4I7(K#k$rVRdQ@_YAhC l&knB7XAC@yxpAH2CcT6WsxikD?7kXvY`~-t=lN7@`~&Hwn(#>@r=9ZPdACFx-GlueO?ZlmD8OBs2mAzu9{>OV literal 0 HcmV?d00001 diff --git a/gpsbabel/reference/ov2-in.ref b/gpsbabel/reference/ov2-in.ref new file mode 100644 index 000000000..908e02e6c --- /dev/null +++ b/gpsbabel/reference/ov2-in.ref @@ -0,0 +1,9 @@ +Mountain Bike Heaven by susy1313 3558.322N 08708.081W 0000000m Mountain Bike Heaven by susy13 a +The Troll by a182pilot & Family 3605.441N 08640.773W 0000000m The Troll by a182pilot & Famil a +Dive Bomber by JoGPS & family 3559.776N 08637.207W 0000000m Dive Bomber by JoGPS & family a +FOSTER by JoGPS & Family 3602.309N 08638.917W 0000000m FOSTER by JoGPS & Family a +Logan Lighthouse by JoGps & Family 3606.731N 08644.506W 0000000m Logan Lighthouse by JoGps & Fa a +Ganier Cache by Susy1313 3603.845N 08647.431W 0000000m Ganier Cache by Susy1313 a +Shy's Hill by FireFighterEng33 3605.266N 08648.584W 0000000m Shy's Hill by FireFighterEng33 a +GittyUp by JoGPS / Warner Parks 3603.449N 08653.519W 0000000m GittyUp by JoGPS / Warner Park a +Inlighting by JoGPS / Warner Parks 3604.967N 08652.037W 0000000m Inlighting by JoGPS / Warner P a diff --git a/gpsbabel/testo b/gpsbabel/testo index 6c0df09a4..d7541498d 100755 --- a/gpsbabel/testo +++ b/gpsbabel/testo @@ -611,6 +611,22 @@ rm -f ${TMPDIR}/an1.out ${PNAME} -i google -f reference/google.js -o an1 -F ${TMPDIR}/an1.out compare ${TMPDIR}/an1.out reference/an1-line-out.ref +# +# TomTom .ov2 tests +# + +rm -f ${TMPDIR}/ov2.out +${PNAME} -i arc -f reference/google.arc -o tomtom -F ${TMPDIR}/ov2.out +compare ${TMPDIR}/ov2.out reference/ov2-arc-out.ref + +rm -f ${TMPDIR}/ov2.out +${PNAME} -i geo -f reference/gl.loc -o tomtom -F ${TMPDIR}/ov2.out +compare ${TMPDIR}/ov2.out reference/ov2-geo-out.ref + +rm -f ${TMPDIR}/ov2.out +${PNAME} -i tomtom -f reference/ov2-geo-out.ref -o gpsutil -F ${TMPDIR}/ov2.out +compare ${TMPDIR}/ov2.out reference/ov2-in.ref + # # XCSV "human readable" tests # diff --git a/gpsbabel/tomtom.c b/gpsbabel/tomtom.c new file mode 100644 index 000000000..abce6e5d4 --- /dev/null +++ b/gpsbabel/tomtom.c @@ -0,0 +1,338 @@ +/* + Read and write TomTom .ov2 files. + + Copyright (C) 2005 Ronald Parker (babeltomtom@parkrrrr.com) and + Robert Lipe (robertlipe@usa.net) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA + + */ + + +/* + This module is based on my reverse-engineering of the .ov2 format, so + it might not be aware of all record types. In particular, I've seen + a type-3 record that may contain additional strings, but since I haven't + seen any of those from a legitimate source, I don't know what they are + supposed to contain. Thus, they are not currently supported. (The one + I saw was due to an errant pair of double-quotes in the input to + makeov2.exe.) -- Ron Parker, 28 April 2005 +*/ + + +#include "defs.h" + +#define MYNAME "TomTom" + +static FILE *file_in; +static FILE *file_out; + +static +arglist_t tomtom_args[] = { + {0, 0, 0, 0 } +}; + +static void +rd_init(const char *fname) +{ + file_in = xfopen(fname, "rb", MYNAME); +} + +static void +rd_deinit(void) +{ + fclose(file_in); +} + +static void +wr_init(const char *fname) +{ + file_out = xfopen(fname, "wb", MYNAME); +} + +static void +wr_deinit(void) +{ + fclose(file_out); +} + +static unsigned long +read_long(FILE * f) +{ + gbuint32 result = 0; + + fread(&result, sizeof (result), 1, f); + return le_read32(&result); +} + +static unsigned char +read_char( FILE *f) +{ + unsigned char result = 0; + fread( &result, 1, 1, f ); + return result; +} + +static void +data_read(void) +{ + unsigned char rectype; + long recsize; + long x; + long y; + char *desc; + waypoint *wpt_tmp; + while (!feof( file_in ) ) { + rectype = read_char( file_in ); + if ( rectype == 1 ) { + /* a block header; ignored on read */ + read_long( file_in ); + read_long( file_in ); + read_long( file_in ); + read_long( file_in ); + read_long( file_in ); + } + else if ( rectype == 2 ) { + recsize = read_long( file_in ); + x = read_long( file_in ); + y = read_long( file_in ); + desc = (char *)xmalloc( recsize - 13 ); + fread( desc, recsize-13, 1, file_in ); + + wpt_tmp = waypt_new(); + + wpt_tmp->longitude = x/100000.0; + wpt_tmp->latitude = y/100000.0; + wpt_tmp->description = desc; + + waypt_add(wpt_tmp); + } + } +} + + +struct hdr{ + waypoint *wpt; +}; + +static +int +compare_lat(const void *a, const void *b) +{ + const struct hdr *wa = a; + const struct hdr *wb = b; + + double difference = wa->wpt->latitude - wb->wpt->latitude; + if ( difference < 0 ) { + return -1; + } + if ( difference ) { + return 1; + } + return 0; +} + +static +int +compare_lon(const void *a, const void *b) +{ + const struct hdr *wa = a; + const struct hdr *wb = b; + + double difference = wa->wpt->longitude - wb->wpt->longitude; + if ( difference < 0 ) { + return -1; + } + if ( difference ) { + return 1; + } + return 0; +} + +static void +write_long( FILE *file, long value ) { + gbuint32 tmp = 0; + le_write32( &tmp, value ); + + fwrite( &tmp, sizeof(tmp), 1, file ); +} + +static void +write_char( FILE *file, unsigned char value ) { + fwrite( &value, 1, 1, file ); +} + +static void +write_string( FILE *file, char *str ) { + fwrite( str, strlen(str), 1, file ); + write_char( file, '\0' ); +} + +struct blockheader { + struct hdr *start; + long count; + long size; + double minlat; + double maxlat; + double minlon; + double maxlon; + struct blockheader *ch1; + struct blockheader *ch2; +}; + +static void +write_blocks( FILE *f, struct blockheader *blocks ) { + int i; + write_char( f, 1 ); + write_long( f, blocks->size ); + write_long( f, blocks->maxlon*100000 ); + write_long( f, blocks->maxlat*100000 ); + write_long( f, blocks->minlon*100000 ); + write_long( f, blocks->minlat*100000 ); + if ( blocks->ch1 ) { + write_blocks( f, blocks->ch1 ); + } + if ( blocks->ch2 ) { + write_blocks( f, blocks->ch2 ); + } + if ( !blocks->ch1 && !blocks->ch2 ) { + for ( i = 0; i < blocks->count; i++ ) { + write_char( f, 2 ); + write_long( f, + strlen( blocks->start[i].wpt->description ) + + 14 ); + write_long( f, blocks->start[i].wpt->longitude*100000); + write_long( f, blocks->start[i].wpt->latitude*100000); + write_string( f, blocks->start[i].wpt->description ); + } + } +} + +static struct blockheader * +compute_blocks( struct hdr *start, int count, + double minlon, double maxlon, double minlat, double maxlat ) { + struct blockheader *newblock; + + newblock = (struct blockheader *)xcalloc( sizeof( *newblock ), 1); + newblock->start = start; + newblock->count = count; + newblock->minlon = minlon; + newblock->maxlon = maxlon; + newblock->minlat = minlat; + newblock->maxlat = maxlat; + newblock->size = 4 * 5 + 1; /* hdr is 5 longs, 1 char */ + if ( count < 20 ) { + int i; + waypoint *wpt = NULL; + + for ( i = 0; i < count; i++ ) { + newblock->size += 4 * 3 + 1; + /* wpt const part 3 longs, 1 char */ + wpt = start[i].wpt; + newblock->size += strlen( wpt->description ) + 1; + } + } + else { + if ( (maxlat-minlat)>(maxlon-minlon)) { + /* split along lats */ + qsort( start, count, sizeof(*start), compare_lat); + newblock->ch1 = compute_blocks( start, count/2, + minlon, maxlon, minlat, + start[count/2].wpt->latitude ); + newblock->ch2 = compute_blocks( start+count/2, + count-count/2, minlon, maxlon, + start[count/2].wpt->latitude, maxlat ); + } + else { + /* split along lons */ + qsort( start, count, sizeof(*start), compare_lon); + newblock->ch1 = compute_blocks( start, count/2, + minlon, start[count/2].wpt->longitude, + minlat, maxlat ); + newblock->ch2 = compute_blocks( start+count/2, + count-count/2, start[count/2].wpt->longitude, + maxlon, minlat, maxlat ); + } + if ( newblock->ch1 ) { + newblock->size += newblock->ch1->size; + } + if ( newblock->ch2 ) { + newblock->size += newblock->ch2->size; + } + } + return newblock; +} + +static void +free_blocks( struct blockheader *block ) { + if ( block->ch1 ) free_blocks( block->ch1 ); + if ( block->ch2 ) free_blocks( block->ch2 ); + xfree( block ); +} + +static void +data_write(void) +{ + int ct = waypt_count(); + struct hdr *htable, *bh; + queue *elem, *tmp; + extern queue waypt_head; + waypoint *waypointp; + double minlon = 200; + double maxlon = -200; + double minlat = 200; + double maxlat = -200; + struct blockheader *blocks = NULL; + + htable = xmalloc(ct * sizeof(*htable)); + bh = htable; + + QUEUE_FOR_EACH(&waypt_head, elem, tmp) { + waypointp = (waypoint *) elem; + bh->wpt = waypointp; + if ( waypointp->longitude > maxlon ) { + maxlon = waypointp->longitude; + } + if ( waypointp->longitude < minlon ) { + minlon = waypointp->longitude; + } + if ( waypointp->latitude > maxlat ) { + maxlat = waypointp->latitude; + } + if ( waypointp->latitude < minlat ) { + minlat = waypointp->latitude; + } + bh ++; + } + + blocks = compute_blocks( htable, ct, minlon, maxlon, minlat, maxlat ); + write_blocks( file_out, blocks ); + free_blocks( blocks ); + + xfree(htable); +} + +ff_vecs_t tomtom_vecs = { + ff_type_file, + FF_CAP_RW_WPT, + rd_init, + wr_init, + rd_deinit, + wr_deinit, + data_read, + data_write, + NULL, + tomtom_args, +}; -- 2.30.2